﻿/*
 *   Copyright 2011 Peter Crossley
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 *  ----------------------------------------------------------------------------
 *  File:       BasicService.cs
 *  Author:     Peter Crossley
 *  ----------------------------------------------------------------------------
 */
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Management;
using System.DirectoryServices;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.Text;
using UEC.Modules.Configuration;
using System.Net;
using NVelocity.App;
using NVelocity;
using NVelocity.Exception;

namespace UEC.Modules.Basic
{
    public class BasicService : ICloudModule
    {
        private CloudManager manager;

        public BasicService(CloudManager manager)
        {
            this.manager = manager;
            manager.CloudServiceEvent += new CloudManager.CloudServiceHandler(ServiceHandler);
        }

        public static string BASIC_SETTINGS
        {
            get { return "BasicSettings"; }
        }

        #region ICloudModule Members

        public string GetServiceName()
        {
            return global::UEC.Properties.Resources.BasicServiceName;
        }

        public System.Windows.Forms.UserControl GetConfigurationControl()
        {
            this.basicSettings = new BasicSettings(this);
            return basicSettings;
        }

        public decimal GetWeight()
        {
            return global::UEC.Properties.Settings.Default.ServiceWeight;
        }

        public CloudManager Manager
        {
            get { return manager; }
        }

        private BasicSettings basicSettings;
        #endregion

        private void ServiceHandler(object sender, CloudManager.CloudServiceEventArgs e)
        {
            //do somthing
            ServiceContext context = manager.ServiceContext;
            manager.LogEvent("ServiceHandler: " + BASIC_SETTINGS + " (" + e.CloudServiceState.ToString() + ")", EventLogEntryType.Information, true);
            switch (e.CloudServiceState)
            {
                case CloudManager.CloudServiceStates.SYSPREP:
                    try
                    {
                        context.ServiceBegin();
                        context.Log("Starting basic sysprep audit.");

                        if (!manager.Reboot)
                        {
                            ProcessUserScripts(context);
                        }
                        else
                        {
                            context.Log("Reboot required, skipping processing of user-data scripts till next restart.");
                        }
                    }
                    finally
                    {
                        context.Log("Finished basic sysprep audit.");
                        context.ServiceEnd();
                    }

                    break;

                case CloudManager.CloudServiceStates.RUN:
                    try
                    {
                        context.ServiceBegin();
                        context.Log("Starting basic system audit.");

                        ManageHostname(context);
                        ManageAdministrator(context);

                        if (!manager.Reboot)
                        {
                            ProcessUserScripts(context);
                        }
                        else
                        {
                            context.Log("Reboot required, skipping processing of user-data scripts till next restart.");
                        }
                    }
                    finally
                    {
                        context.Log("Finished basic system audit.");
                        context.ServiceEnd();
                    }

                    break;

                case CloudManager.CloudServiceStates.PROPERTIES:
                    try
                    {
                        context.ServiceBegin();
                        context.Log("Starting basic properties management.");
                        SetAdministratorProperties(context);
                        SetMachineProperties(context);
                    }
                    finally
                    {
                        context.Log("Finished basic properties management.");
                        context.ServiceEnd();
                    }
                    break;
            }
            context.FlushContext();
        }

        private void SetAdministratorProperties(ServiceContext context)
        {
            //Check if we need to generate a new system administrator password
            string adminUser = (string)manager.GetValue(BASIC_SETTINGS, "AdminUser", Properties.Settings.Default.AdminUser);
            try
            {
                context.Log("Setting user management properties [AdminUser, AdminUserPassword].");
                string pwd = _GetAdminPassword(context);
                string secLog = "\n\t ** System Credentials **\n";
                secLog += "\t   User: " + adminUser + "\n";
                secLog += "\t   Password: " + pwd + "\n";
                manager.WriteConsole(secLog, false);

                manager.SystemProperties.Add("AdminUser", adminUser);
                manager.SystemProperties.Add("AdminUserPassword", pwd);
            }
            catch (Exception ex)
            {
                manager.LogEvent("Unable to set properties for user management.\n " + ex.ToString(), EventLogEntryType.Warning);
            }
        }

        private void ManageAdministrator(ServiceContext context)
        {
            try
            {
                //Check if we need to generate a new system administrator password
                string adminUser = (string)manager.GetValue(BASIC_SETTINGS, "AdminUser", Properties.Settings.Default.AdminUser);
                string pwd = _GetAdminPassword(context);
                SetUserPasswordWMI(adminUser, pwd);               
            }
            catch (Exception ex)
            {
                manager.LogEvent("Unable to audit system credentials.\n " + ex.ToString(), EventLogEntryType.Warning);
            }
        }

        private string _GetAdminPassword(ServiceContext context)
        {
            //Check if we need to generate a new system administrator password
            string adminUser = (string)manager.GetValue(BASIC_SETTINGS, "AdminUser", Properties.Settings.Default.AdminUser);
            bool IsResetAdminPwd = manager.GetValueAsBoolean(BASIC_SETTINGS, "IsResetAdminPwd", Properties.Settings.Default.IsResetAdminPwd);
            string secToken = (string)manager.GetValue(BASIC_SETTINGS, "SecToken", null);
            if (IsResetAdminPwd || secToken == null)
            {
                context.Log("Generating random password.");
                string password = GetRandomPassword();
                secToken = Encrypt(password);
                manager.SetValue(BASIC_SETTINGS, "SecToken", secToken);                
                manager.SetValue(BASIC_SETTINGS, "IsResetAdminPwd", false);
            }

            string pwd = Decrypt(secToken);
            return Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(pwd));
        }

        private void SetMachineProperties(ServiceContext context)
        {
            try {
                string hostname = _GetDNSHostname();
                string netbios = _GetNetBIOSHostname();

                bool IsForceHostname = manager.GetValueAsBoolean(BASIC_SETTINGS, "IsForceHostname", Properties.Settings.Default.IsForceHostname);
                if (IsForceHostname)
                {
                    context.Log("Setting hostname management properties [Hostname, NetbiosName].");
                    hostname = netbios = _GetHostname(context);
                    if (hostname == null)
                    {
                        hostname = netbios = _GetNetbiosName(context);
                    }
                    if (manager.GetValueAsBoolean(BASIC_SETTINGS, "OverrideNetBIOS", false) || hostname.Length > 15)
                    {
                        netbios = _GetNetbiosName(context);
                    }

                }

                context.Log("Hostname: " + hostname);
                context.Log("NetbiosName: " + netbios);
                

                manager.SystemProperties.Add("Hostname", hostname);
                manager.SystemProperties.Add("NetbiosName", netbios);
            }
            catch (Exception ex)
            {
                manager.LogEvent("Unable to set hostname properties.\n " + ex.ToString(), EventLogEntryType.Warning);
            }

        }

        private void ManageHostname(ServiceContext context)
        {
            try
            {
                //Should we enforce hostname settings?
                bool IsForceHostname = manager.GetValueAsBoolean(BASIC_SETTINGS, "IsForceHostname", Properties.Settings.Default.IsForceHostname);
                bool IsCustomHostname = manager.GetValueAsBoolean(BASIC_SETTINGS, "IsCustomHostname", Properties.Settings.Default.IsCustomHostname);
                string CustomHostname = (string)manager.GetValue(BASIC_SETTINGS, "CustomHostname", null);
                string hostname = _GetDNSHostname();
                string netbios = _GetNetBIOSHostname();
                if (IsForceHostname)
                {
                    hostname = netbios = _GetHostname(context);
                    if (hostname == null)
                    {
                        hostname = netbios = _GetNetbiosName(context);
                    }
                    if (manager.GetValueAsBoolean(BASIC_SETTINGS, "OverrideNetBIOS", false) || hostname.Length > 15)
                    {
                        netbios = _GetNetbiosName(context);
                    }

                    string computerName = _GetDNSHostname();
                    string computerNetBIOSName = _GetNetBIOSHostname();

                    if (!String.Equals(computerName, hostname, StringComparison.CurrentCultureIgnoreCase) && 
                        !String.Equals(computerNetBIOSName, netbios, StringComparison.CurrentCultureIgnoreCase))
                    {
                        context.Log("Setting hostname: " + hostname + " (was: " + computerName  + ")");
                        context.Log("Setting NetBIOS name: " + netbios + " (was: " + computerNetBIOSName + ")");
                        if (!manager.Debug)
                        {
                            SetComputerNameEx(COMPUTER_NAME_FORMAT.ComputerNamePhysicalDnsHostname, hostname);
                            SetComputerNameEx(COMPUTER_NAME_FORMAT.ComputerNamePhysicalNetBIOS, netbios);
                            manager.Reboot = true;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                manager.LogEvent("Unable to audit hostname.\n " + ex.ToString(), EventLogEntryType.Warning);
            }
        }

        private string _GetHostname(ServiceContext context) {
            string hostname = null;
            bool IsCustomHostname = manager.GetValueAsBoolean(BASIC_SETTINGS, "IsCustomHostname", Properties.Settings.Default.IsCustomHostname);
            bool IsRandomGenHost = manager.GetValueAsBoolean(BASIC_SETTINGS, "IsRandomGenHost", false);
            string CustomHostname = (string)manager.GetValue(BASIC_SETTINGS, "CustomHostname", null);
            if ((IsCustomHostname && !String.IsNullOrEmpty(CustomHostname) && CustomHostname.Length <= 15) || (IsRandomGenHost && !String.IsNullOrEmpty(CustomHostname)))
            {
                hostname = CustomHostname;
            }
            else
            {                       
                string mdHostname = (string)manager.GetValue(BASIC_SETTINGS, "MetaDataHostname", "local-hostname");
                hostname = manager.GetMetaDataValue("/" + mdHostname);                
                if (hostname == null)
                {
                    context.Log("Generating random hostname.");
                    hostname = GetRandomHostname();
                    manager.SetValue(BASIC_SETTINGS, "IsRandomGenHost", true);
                    manager.SetValue(BASIC_SETTINGS, "CustomHostname", hostname);
                }
                else
                {
                    manager.SetValue(BASIC_SETTINGS, "IsRandomGenHost", false);
                    manager.SetValue(BASIC_SETTINGS, "IsCustomHostname", false);
                    hostname = hostname.Substring(0, hostname.IndexOf('.'));
                    string hostnamePrefix = (string)manager.GetValue(BASIC_SETTINGS, "HostnamePrefix", "");
                    if (hostnamePrefix.Length + hostname.Length > 15 && hostname.Length <= 12)
                    {
                        hostnamePrefix = hostnamePrefix.Substring(0, 1) + hostnamePrefix.Substring(hostnamePrefix.Length - 1) + "-";
                        hostname = hostnamePrefix + hostname;
                    }                   
                }
            }
            return hostname;
        }

        private string _GetNetbiosName(ServiceContext context)
        {
            string mdNetbios = (string)manager.GetValue(BASIC_SETTINGS, "MetaDataNetBios", "instance-id");
            return manager.GetMetaDataValue("/" + mdNetbios);
        }

        private string _GetDNSHostname() {
            uint size = 260;
            StringBuilder computerName = new StringBuilder((int)size);
            GetComputerNameEx(COMPUTER_NAME_FORMAT.ComputerNamePhysicalDnsHostname, computerName, ref size);
            return computerName.ToString().Trim();
        }

        private string _GetNetBIOSHostname()
        {
            uint size = 260;
            StringBuilder computerName = new StringBuilder((int)size);
            GetComputerNameEx(COMPUTER_NAME_FORMAT.ComputerNamePhysicalNetBIOS, computerName, ref size);
            return computerName.ToString().Trim(); ;
        }    

        private void ProcessUserScripts(ServiceContext context)
        {
            try
            {
                //check to see if we need to process user scripts
                bool IsProcessUserDataScripts = manager.GetValueAsBoolean(BASIC_SETTINGS, "IsProcessUserDataScripts", Properties.Settings.Default.IsProcessUserDataScripts);
                ProcessScript[] scripts = manager.CloudInit.Scripts.ToArray();
                Array.Sort(scripts, new ProcessScriptCompare());
                if (IsProcessUserDataScripts && scripts != null)
                {
                    List<string> runonce = new List<string>(((string)manager.GetValue(BASIC_SETTINGS, "RunOnceScripts", "")).Split(','));
                    foreach (ProcessScript ps in scripts)
                    {
                        try
                        {
                            if (ProcessScript.ScriptType.RUN_ONCE == ps.Type && !runonce.Contains(ps.Name) && !manager.IsUserMode)
                            {
                                context.Log("ProcessScript: " + ps.Name + ", " + ps.Type);
                                if (!manager.Debug)
                                {
                                    ExecProcessScript(ps);
                                }
                                runonce.Add(ps.Name);
                            }
                            else if (manager.IsUserMode && ProcessScript.ScriptType.USER == ps.Type)
                            {
                                context.Log("ProcessScript: " + ps.Name + ", " + ps.Type);
                                if (!manager.Debug)
                                {
                                    ExecProcessScript(ps);
                                }
                            }
                            else if (ProcessScript.ScriptType.SYSTEM == ps.Type)
                            {
                                context.Log("ProcessScript: " + ps.Name + ", " + ps.Type);
                                if (!manager.Debug)
                                {
                                    ExecProcessScript(ps);
                                }
                            }
                        }
                        catch (Exception ex1)
                        {
                            manager.LogEvent("Error processing user script: " + ps.Name + "\n" + ex1.ToString(), EventLogEntryType.Warning, true);
                        }
                    }
                    manager.SetValue(BASIC_SETTINGS, "RunOnceScripts", string.Join(",", runonce.ToArray()));
                }
            }
            catch (Exception ex)
            {
                manager.LogEvent("Unable to process user scripts.\n " + ex.ToString(), EventLogEntryType.Warning);
            }
        }


        /**** SUPPORT *****/

        private static String GetRandomHostname() {
            return GetRandomHostname(15, null);
        }

        private static String GetRandomHostname(int size, string prefix)
        {
            if (prefix == null) {
                prefix = "WIN-";
            }
            String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
            String host = "";
            TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1));
            int timestamp = (int)t.TotalSeconds;
            Random r = new Random(timestamp);
            for (int i = 0; i < (size - prefix.Length); i++)
            {
                host += chars.ToCharArray()[r.Next(0, chars.Length)];
            }
            return prefix + host;
        }

        private static String validLower = "abcdefghijklmnopqrstuvwxyz";
        private static String validUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        private static String validDigits = "0123456789";
        private static String validSymbols = "*().-_[]`~|@$%^&?:!#{}\\;'<>,/+=";
        private static int lowerWeight = 5;
        private static int upperWeight = 5;
        private static int digitWeight = 4;
        private static int symbolWeight = 3;

        private static int minLength = 10;
        private static int maxLength = 10;

        private static String GetRandomPassword()
        {
            TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1));
            int timestamp = (int)t.Milliseconds;
            Random _rng = new Random(timestamp);

            int passwordLength = minLength + _rng.Next(maxLength - minLength + 1);
            String password;

            do
            {
                password = "";
                for (int i = 0; i < passwordLength; i++)
                {
                    password += _GenerateRandomChar(_rng);
                }
            } while (!_IsPasswordValid(password));

            return password;
        }

        /**
     * Generate a random character using the weighted buckets
     * @return a random char
     */
        private static char _GenerateRandomChar(Random _rng)
        {
            int totalWeight = lowerWeight;
            char c = validLower.ToCharArray()[_rng.Next(validLower.Length)];

            totalWeight += upperWeight;
            if (_rng.Next(totalWeight) < upperWeight)
            {
                c = validUpper.ToCharArray()[_rng.Next(validUpper.Length)];
            }

            totalWeight += digitWeight;
            if (_rng.Next(totalWeight) < digitWeight)
            {
                c = validDigits.ToCharArray()[_rng.Next(validDigits.Length)];
            }

            totalWeight += symbolWeight;
            if (_rng.Next(totalWeight) < symbolWeight)
            {
                c = validSymbols.ToCharArray()[_rng.Next(validSymbols.Length)];
            }

            return c;
        }

        /**
        * Checks the password against the following requirement:
        * Password must contain characters from 3 of the following 4 sets:
        * 1. Uppercase
        * 2. lowercase
        * 3. digits
        * 4. symbols
        * @return
        */
        private static bool _IsPasswordValid(String password)
        {
            int hasUpper = 0;
            int hasLower = 0;
            int hasDigit = 0;
            int hasSymbol = 0;

            if (password.Length < minLength)
            {
                return false;
            }

            for (int i = 0; i < password.Length; i++)
            {
                if (Char.IsLower(password.ToCharArray()[i]))
                {
                    hasLower = 1;
                }
                else if (Char.IsUpper(password.ToCharArray()[i]))
                {
                    hasUpper = 1;
                }
                else if (Char.IsDigit(password.ToCharArray()[i]))
                {
                    hasDigit = 1;
                }
                else
                {
                    for (int j = 0; j < validSymbols.Length; j++)
                    {
                        if (password.ToCharArray()[i] == validSymbols.ToCharArray()[j])
                        {
                            hasSymbol = 1;
                            break;
                        }
                    }
                }

                if (hasUpper + hasLower + hasDigit + hasSymbol >= 3)
                {
                    return true;
                }
            }

            return false;
        }

        private void SetUserPasswordWMI(string user, string password)
        {
            try
            {
                DirectoryEntry directoryEntry = new DirectoryEntry(string.Format("WinNT://./{0}", user));
                directoryEntry.Invoke("SetPassword", password);
            }
            catch (Exception ex)
            {
                manager.WriteConsole("SetUserPassword (error):\n" + ex.Message);
            }

        }

        /** Deprecated **/
        private void SetUserPassword(string user, string password)
        {
            Process process = new Process();
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.FileName = "net.exe";
            process.StartInfo.Arguments = "user " + user + " \"" + password + "\"";
            process.StartInfo.CreateNoWindow = true;
            process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardError = true;
            process.Start();
            string output = process.StandardOutput.ReadToEnd();
            if (output != null && output != "")
            {
                manager.WriteConsole("SetUserPassword (output):\n" + output);
            }
            string error = process.StandardError.ReadToEnd();
            if (error != null && error != "")
            {
                manager.WriteConsole("SetUserPassword (error):\n" + error);
            }
            process.WaitForExit();
        }

        private void ProcessScriptTemplate(string file)
        {
            if (!File.Exists(file))
            {
                manager.LogEvent("info : no script file defined for execution.", EventLogEntryType.Information, true);
                return;
            }


            /*
	        * setup
	        */
            Velocity.Init();

            /*
            *  Make a context object and populate with the data.  This 
            *  is where the Velocity engine gets the data to resolve the
            *  references (ex. $list) in the template
            */
            VelocityContext context = new VelocityContext();
            foreach (string type in manager.GetTypes())
            {
                context.Put(type, ContextTypeMap(type));
            }
            context.Put("system", manager.SystemProperties);

            try
            {
                TextWriter streamWriter = null;
                string templatePath = Path.Combine(Environment.CurrentDirectory, Path.GetFileName(file) + ".vm");
                try
                {
                    File.Copy(file, templatePath, true);
                    streamWriter = new StreamWriter(file);
                    Velocity.MergeTemplate(Path.GetFileName(templatePath), context, streamWriter);
                    manager.LogEvent("Processed script template [" + file + "]", EventLogEntryType.Information);
                }
                finally
                {
                    if (streamWriter != null)
                    {
                        streamWriter.Close();
                    }
                    File.Delete(templatePath);
                }
            }
            catch (ResourceNotFoundException)
            {
                manager.LogEvent("error : cannot find template " + file, EventLogEntryType.Error, true);
            }
            catch (ParseErrorException pee)
            {
                manager.LogEvent("Syntax error in template " + file + ":" + pee, EventLogEntryType.Error, true);
            }

            /*
            *  Now have the template engine process your template using the
            *  data placed into the context.  Think of it as a  'merge' 
            *  of the template and the data to produce the output stream.
            */

        }

        private void ExecProcessScript(ProcessScript script)
        {
            string tempFile = Path.GetTempFileName();
            try
            {
                //make it a script
                tempFile += "." + script.Extention;
                if (!String.IsNullOrEmpty(script.Source))
                {
                    try
                    {
                        Uri serviceUri = new Uri(script.Source);
                        WebClient downloader = new WebClient();
                        downloader.DownloadFile(serviceUri, tempFile);
                    }
                    catch (Exception ex)
                    {
                        manager.LogEvent("ProcessScript Download: \n" + ex.ToString(), EventLogEntryType.Error, true);
                    }
                }
                else
                {
                    FileStream fileStream = new FileStream(tempFile, FileMode.Create, FileAccess.Write);
                    StreamWriter fileWriter = new StreamWriter(fileStream);
                    try
                    {
                        fileWriter.BaseStream.Seek(0, SeekOrigin.End);
                        fileWriter.Write(script.Script);
                    }
                    catch (IOException ex)
                    {
                        manager.LogEvent("ProcessScript: \n" + ex.ToString(), EventLogEntryType.Error, true);
                    }
                    finally
                    {
                        fileWriter.Close();
                    }
                }
                
                //Process the script and check to make sure we have all variables replaced if needed
                ProcessScriptTemplate(tempFile);

                string command = tempFile;
                string args = null;
                if (!String.IsNullOrEmpty(script.Interpreter))
                {
                    command = script.Interpreter;
                    args = tempFile;
                }

                Process process = new Process();
                process.StartInfo.FileName = command;
                if (!String.IsNullOrEmpty(script.Arguments))
                {
                    args = script.Arguments.Replace("{script}", tempFile);
                    process.StartInfo.Arguments = args;
                }

                if (script.Elevate)
                {
                    process.StartInfo.Verb = "runas"; // indicates to elevate privileges
                    process.StartInfo.UseShellExecute = true; //needed so we can do a runas verb
                }
                else
                {
                    process.StartInfo.UseShellExecute = false;
                }

                if (!script.Interactive)
                {
                    process.StartInfo.CreateNoWindow = true;
                    process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                }
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;
                manager.LogEvent("Process [" + script.Name + ", " + script.Type + "]", EventLogEntryType.Information, true);
                process.Start();
                Stream os = null;
                try {
                    os = new BufferedStream(process.StandardOutput.BaseStream);
                    manager.WriteConsole(os);
                } finally {
                    if (os != null) {
                        os.Close();
                    }
                }
                try
                {
                    os = new BufferedStream(process.StandardError.BaseStream);
                    manager.WriteConsole(os);
                }
                finally
                {
                    if (os != null)
                    {
                        os.Close();
                    }
                }
                /**string output = process.StandardOutput.ReadToEnd();
                if (output != null && output != "")
                {
                    manager.LogEvent("Process [" + script.Name + ", " + script.Type + "] Output:\n" + output, EventLogEntryType.Information, true);
                }
                string error = process.StandardError.ReadToEnd();
                if (error != null && error != "")
                {
                    manager.LogEvent("Process [" + script.Name + ", " + script.Type + "] Error:\n" + error, EventLogEntryType.Error, true);
                }**/
                process.WaitForExit();
                
                if (script.AllowReboot && process.ExitCode != 0) {
                    manager.LogEvent("Process [" + script.Name + ", " + script.Type + "] requires reboot after system has been managed.", EventLogEntryType.Information, true);
                    manager.Reboot = true;
                }               
            }
            finally
            {
                //clean up the file
                File.Delete(tempFile);
            }
        }

        private IDictionary<string, object> ContextTypeMap(string type)
        {
            IDictionary<string, object> dict = new Dictionary<string, object>();
            foreach (string name in manager.GetValueNames(type))
            {
                dict.Add(name, manager.GetValue(type, name, null));
            }
            return dict;
        }

        private byte[] KEY_HASH
        {
            get
            {
                string key = "";
                ManagementClass managClass = new ManagementClass("win32_processor");
                ManagementObjectCollection managCollec = managClass.GetInstances();
                ManagementObjectCollection.ManagementObjectEnumerator manObj = managCollec.GetEnumerator();
                if (manObj.MoveNext())
                {
                    key += manObj.Current.Properties["processorID"].Value.ToString();
                }

                string drive = Environment.CurrentDirectory.Substring(0, 1);
                ManagementObject dsk = new ManagementObject(@"win32_logicaldisk.deviceid=""" + drive + @":""");
                dsk.Get();
                key += dsk["VolumeSerialNumber"].ToString();
                //make the key bigger
                while (key.Length < 16)
                {
                    key += key;
                }

                System.Text.Encoding enc = System.Text.Encoding.Unicode;
                return enc.GetBytes(key.Substring(0, 16));
            }
        }

        private byte[] KEY_VECTOR
        {
            get
            {
                System.Text.Encoding enc = System.Text.Encoding.Unicode;
                string sv = (string)manager.GetValue(BASIC_SETTINGS, "SecVector", null);
                byte[] v = null;
                if (sv == null)
                {
                    AesCryptoServiceProvider cryptoProvider = new AesCryptoServiceProvider();
                    cryptoProvider.Padding = PaddingMode.Zeros;
                    v = cryptoProvider.IV;
                    sv = enc.GetString(v);
                    manager.SetValue(BASIC_SETTINGS, "SecVector", sv);
                }
                else
                {
                    v = enc.GetBytes(sv);
                }

                return v;
            }
        }

        /// <summary>
        /// Encrypt a string.
        /// </summary>
        /// <param name="originalString">The original string.</param>
        /// <returns>The encrypted string.</returns>
        /// <exception cref="ArgumentNullException">This exception will be thrown when the original string is null or empty.</exception>
        private string Encrypt(string originalString)
        {

            if (String.IsNullOrEmpty(originalString))
            {
                throw new ArgumentNullException("The string which needs to be encrypted can not be null.");
            }

            AesCryptoServiceProvider cryptoProvider = new AesCryptoServiceProvider();
            cryptoProvider.Padding = PaddingMode.Zeros;
            MemoryStream memoryStream = new MemoryStream();
            CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoProvider.CreateEncryptor(KEY_HASH, KEY_VECTOR), CryptoStreamMode.Write);

            StreamWriter writer = new StreamWriter(cryptoStream);
            writer.Write(originalString);
            writer.Flush();
            cryptoStream.FlushFinalBlock();
            writer.Flush();

            return Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);

        }



        private string Decrypt(string cryptedString)
        {
            if (String.IsNullOrEmpty(cryptedString))
            {
                throw new ArgumentNullException("The string which needs to be decrypted can not be null.");
            }

            AesCryptoServiceProvider cryptoProvider = new AesCryptoServiceProvider();
            cryptoProvider.Padding = PaddingMode.Zeros;
            MemoryStream memoryStream = new MemoryStream(Convert.FromBase64String(cryptedString));
            CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoProvider.CreateDecryptor(KEY_HASH, KEY_VECTOR), CryptoStreamMode.Read);
            StreamReader reader = new StreamReader(cryptoStream);

            string data = reader.ReadToEnd();
            return data.Substring(0, data.IndexOf('\0'));
        }

        //P/Invoke signature
        [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto, ThrowOnUnmappableChar=true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool SetComputerNameEx(COMPUTER_NAME_FORMAT nameType, [MarshalAs(UnmanagedType.LPTStr)] string lpBuffer);

        [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto, ThrowOnUnmappableChar = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool GetComputerNameEx(COMPUTER_NAME_FORMAT NameType, [Out] StringBuilder lpBuffer, ref uint lpnSize);

        private enum COMPUTER_NAME_FORMAT : int
        {
            ComputerNameNetBIOS,
            ComputerNameDnsHostname,
            ComputerNameDnsDomain,
            ComputerNameDnsFullyQualified,
            ComputerNamePhysicalNetBIOS,
            ComputerNamePhysicalDnsHostname,
            ComputerNamePhysicalDnsDomain,
            ComputerNamePhysicalDnsFullyQualified,
            ComputerNameMax
        }
    }
}
